using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.ServiceModel;
using System.Threading;
using AutoMapper;
using Core;
using IglooCoder.Commons.WcfNhibernate;
using Infrastucture;
using WcfService1.DTO;
using Omu.ValueInjecter;

namespace WcfService1
{
    // NOTE: You can use the "Rename" command on the "Refactor" SalesReturn to change the class name "SalesReturnServices" in code, svc and config file together.
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    [NHibernateContext]
    public class SalesReturnServices : ISalesReturnServices
    {
        private readonly SalesReturnRepository SalesReturnRepository;
        private readonly InventoryWarehouseRepository _iwRepository;
        private readonly MutasiStokRepository _mutasiStokRepository;
        private readonly WcfSessionStorage _session;
        private readonly SalesInvoiceRepository _siRepository;
        private InventoryRepository InventoryRep;
        private readonly  HppRepository _hppRepository;
        private readonly JurnalRepository _jurnalRepository;
        private readonly CoaRepository _coaRepository;

        public SalesReturnServices()
        {
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
            _session = new WcfSessionStorage();
            SalesReturnRepository = new SalesReturnRepository(_session);
            InventoryRep = new InventoryRepository(_session);
            _mutasiStokRepository = new MutasiStokRepository(_session);
            _iwRepository = new InventoryWarehouseRepository(_session);
            _siRepository = new SalesInvoiceRepository(_session);
            _hppRepository = new HppRepository(_session); 
            _jurnalRepository = new JurnalRepository(_session);
            _coaRepository = new CoaRepository(_session);

        }

        #region ISalesReturnServices Members

        public List<SalesReturnDTO> GetList(int page, out long recordCount, string sortProperty = null,
                                            string sortType = "ASC")
        {
            var SalesReturns = new List<SalesReturn>();

            if (string.IsNullOrEmpty(sortProperty))
            {
                SalesReturns = SalesReturnRepository.GetDataPaged(page, 30, "SRDate", "DESC").ToList();
            }
            else
            {
                SalesReturns = SalesReturnRepository.GetDataPaged(page, 30, sortProperty, sortType).ToList();
            }

            recordCount = SalesReturnRepository.RecordCount;

            var Results = new List<SalesReturnDTO>();

            Results = (from s in SalesReturns
                       select new SalesReturnDTO
                                  {
                                      SRNo = s.SRNo,
                                      SRDate = s.SRDate,
                                      Customer =
                                          new CustomerDTO
                                              {
                                                  CustomerId = s.Customer.CustomerId,
                                                  CustomerName = s.Customer.CustomerName
                                              }
                                      ,
                                      Description = s.Description,
                                      SalesInvoice = new SalesInvoiceDTO(){SINo = s.SalesInvoice.SINo},
                                      CostCenter = s.CostCenter.ConvertToDTO<CostCenter,CostCenterDTO>()
                                  }).ToList();

            return Results;
        }

        public bool Save(SalesReturnDTO SalesReturnDTO)
        {
            bool res;
            bool IsNew = false;
            try
            {
                SalesReturn SalesReturn = SalesReturnRepository.GetDataOne(x => x.SRNo == SalesReturnDTO.SRNo);
                if (SalesReturn == null)
                {
                    SalesReturn = new SalesReturn();
                    IsNew = true;
                }

                SalesReturnRepository.ExecuteTransactionNative(() =>
                                                                   {
                                                                       if (!IsNew)
                                                                       {
                                                                           //if (SalesReturn.Status == (int)Status.Void || SalesReturn.Status == (int)Status.Close)
                                                                           //    throw new Exception("Tidak bisa di Update Status sudah Close atau Void!");
                                                                           _mutasiStokRepository.DeleteMutasiStok(
                                                                              SalesReturn.SRNo);
                                                                           SalesReturnRepository.Delete(SalesReturn);
                                                                           var SR = new SalesReturn();
                                                                           SR.modified_date = DateTime.Now;
                                                                           SR.created_date = SalesReturn.created_date;
                                                                           Mapper.Map(SalesReturnDTO, SR);
                                                                           foreach (var srDetail in SR.SalesReturnDetails)
                                                                               srDetail.SalesReturn = SR;
                                                                           SalesReturnRepository.Session.SaveOrUpdateCopy(SR);

                                                                           foreach (SalesReturnDetail salesReturnDetail in
                                                                             SR.SalesReturnDetails)
                                                                           {
                                                                               salesReturnDetail.Warehouse =
                                                                                   SalesReturn.Warehouse;
                                                                               //Mutasi Masuk
                                                                               var mutasiStok = new MutasiStok();
                                                                               mutasiStok.ReferenceNumber = SalesReturn.SRNo;
                                                                               mutasiStok.SerialNumber =
                                                                                   salesReturnDetail.Inventory.SerialNumber;
                                                                               mutasiStok.TglMutasi = SalesReturn.SRDate.ToDateTime();
                                                                               mutasiStok.Type = SalesReturn.GetType().Name;
                                                                               mutasiStok.QtyMasuk = salesReturnDetail.QtyUnit;
                                                                               MutasiStok mutasiLast = _mutasiStokRepository.FindLastMutasi(
                                                                                       mutasiStok.SerialNumber,
                                                                                       salesReturnDetail.Warehouse.KodeWarehouse,
                                                                                       mutasiStok.TglMutasi
                                                                                       );
                                                                               mutasiStok.QtySisa = mutasiLast.QtySisa + mutasiStok.QtyMasuk;
                                                                               mutasiStok.SheetMasuk = salesReturnDetail.Tinggi;
                                                                               mutasiStok.SheetSisa = mutasiLast.SheetSisa + mutasiStok.SheetMasuk;
                                                                               mutasiStok.PanjangMasuk = salesReturnDetail.Panjang;
                                                                               mutasiStok.PanjangSisa = mutasiLast.PanjangSisa + mutasiStok.PanjangMasuk;
                                                                               mutasiStok.LebarMasuk = salesReturnDetail.Lebar;
                                                                               mutasiStok.LebarSisa = mutasiLast.LebarSisa + mutasiStok.LebarMasuk;
                                                                               mutasiStok.KodeWarehouse =
                                                                                   SR.Warehouse.KodeWarehouse;
                                                                               _mutasiStokRepository.SaveOrUpdate(mutasiStok,salesReturnDetail.Inventory.KodePallet);
                                                                           }
                                                                       }else
                                                                       {
                                                                           Mapper.Map(SalesReturnDTO, SalesReturn);
                                                                           //Untuk New entry dari transaksi bukan dari email
                                                                           if (String.IsNullOrEmpty(SalesReturn.SRNo))
                                                                                    SalesReturn.SRNo = GetLastSRNo(SalesReturnDTO.CostCenter);
                                                                           foreach (var srDetail in SalesReturn.SalesReturnDetails)
                                                                           {
                                                                               srDetail.SalesReturn = SalesReturn;
                                                                               srDetail.SalesReturnDetailId = 0;
                                                                           }

                                                                           foreach (SalesReturnDetail salesReturnDetail in
                                                                             SalesReturn.SalesReturnDetails)
                                                                           {

                                                                               salesReturnDetail.Warehouse =
                                                                                  SalesReturn.Warehouse;
                                                                               //Mutasi Masuk
                                                                               var mutasiStok = new MutasiStok();
                                                                               mutasiStok.ReferenceNumber = SalesReturn.SRNo;
                                                                               mutasiStok.SerialNumber =
                                                                                   salesReturnDetail.Inventory.SerialNumber;
                                                                               mutasiStok.TglMutasi = SalesReturn.SRDate.ToDateTime();
                                                                               mutasiStok.Type = SalesReturn.GetType().Name;
                                                                               mutasiStok.QtyMasuk =
                                                                                   salesReturnDetail.QtyUnit;
                                                                               MutasiStok mutasiLast =
                                                                                   _mutasiStokRepository.FindLastMutasi(
                                                                                       mutasiStok.SerialNumber,
                                                                                       salesReturnDetail.Warehouse.KodeWarehouse,
                                                                                       mutasiStok.TglMutasi
                                                                                       );
                                                                               mutasiStok.QtySisa = mutasiLast.QtySisa + mutasiStok.QtyMasuk;
                                                                               mutasiStok.SheetMasuk = salesReturnDetail.Tinggi;
                                                                               mutasiStok.SheetSisa = mutasiLast.SheetSisa + mutasiStok.SheetMasuk;
                                                                               mutasiStok.PanjangMasuk = salesReturnDetail.Panjang;
                                                                               mutasiStok.PanjangSisa = mutasiLast.PanjangSisa + mutasiStok.PanjangMasuk;
                                                                               mutasiStok.LebarMasuk = salesReturnDetail.Lebar;
                                                                               mutasiStok.LebarSisa = mutasiLast.LebarSisa + mutasiStok.LebarMasuk;
                                                                               mutasiStok.KodeWarehouse =
                                                                                   SalesReturn.Warehouse.KodeWarehouse;
                                                                               _mutasiStokRepository.SaveOrUpdate(mutasiStok,salesReturnDetail.Inventory.KodePallet);
                                                                           }
                                                                           SalesReturnRepository.SaveOrUpdate(SalesReturn);
                                                                       }
                                                                     
                                                                     //Update Status SalesInvoice
                                                                       _siRepository.UpdateStatus(SalesReturn.SalesInvoice.SINo,1);
                                                                   }
                    );

                res = true;
            }
            catch (Exception)
            {
                
                res = false;
                throw;
            }

            return res;
        }


        public bool Delete(SalesReturnDTO SalesReturnDto)
        {
            bool res;
            try
            {
                SalesReturn SalesReturn = SalesReturnRepository.GetDataOne(x => x.SRNo == SalesReturnDto.SRNo);
                SalesReturnRepository.ExecuteTransactionNative(
                    () =>
                        {
                            _mutasiStokRepository.DeleteMutasiStok(SalesReturn.SRNo);
                            _siRepository.UpdateStatus(SalesReturn.SalesInvoice.SINo,0);
                            SalesReturn.Status = 2;
                            SalesReturnRepository.SaveOrUpdate(SalesReturn);
                        }
                    );

                res = true;
            }
            catch (Exception)
            {
                res = false;
                throw;
              
            }

            return res;
        }


        public List<SalesReturnDTO> GetListByCriteria(Dictionary<String, Object> criteria, string expression, int page,
                                                      out long recordCount)
        {
            var SalesReturns = new List<SalesReturn>();
            var Results = new List<SalesReturnDTO>();
            SalesReturns = SalesReturnRepository.GetDataPaged(criteria, expression, page, 30).ToList();
            recordCount = SalesReturnRepository.RecordCount;
            SalesReturns.ForEach(x =>
                                     {
                                         var SalesReturnDto = new SalesReturnDTO();
                                         Mapper.Map(x, SalesReturnDto);
                                         Results.Add(SalesReturnDto);
                                     }
                );

            return Results;
        }


        public List<SalesReturnDTO> GetAllSalesReturn()
        {
            List<SalesReturn> SalesReturns = SalesReturnRepository.FetchAll().ToList();
            var Results = new List<SalesReturnDTO>();
            SalesReturns.ForEach(x =>
                                     {
                                         var SalesReturnDto = new SalesReturnDTO();
                                         Mapper.Map(x, SalesReturnDto);
                                         Results.Add(SalesReturnDto);
                                     }
                );

            return Results;
        }


        public SalesReturnDTO GetSalesReturn(String kodeSalesReturn)
        {
            SalesReturn salesReturn = SalesReturnRepository.GetDataOne(x => x.SRNo == kodeSalesReturn);
            var salesReturnDto = new SalesReturnDTO();
            salesReturnDto.InjectFrom<CloneInjection>(salesReturn);
            return salesReturnDto;
        }

        public List<SalesReturnDTO> GetSalesReturnList(string kodeSalesReturn)
        {
            List<SalesReturn> SalesReturns =
                SalesReturnRepository.GetDataList(x => x.SRNo.Contains(kodeSalesReturn)).ToList();
            var Results = new List<SalesReturnDTO>();
            SalesReturns.ForEach(x =>
                                     {
                                         var salesReturnDto = new SalesReturnDTO();
                                         salesReturnDto.InjectFrom<CloneInjection>(x);
                                         Results.Add(salesReturnDto);
                                     }
                );

            return Results;
        }

        public string GetLastSRNo(CostCenterDTO costCenter)
        {
            var appParamServices = new AppParamServices();
            AppParamDTO param = appParamServices.GetAppParamList("KodeSR").FirstOrDefault();
            string res = SalesReturnRepository.GetMaxNumber(costCenter.CostCenterId,param.AppParamValue);
          
            if (string.IsNullOrEmpty(res))
                res = param.AppParamValue;

            string formatSO = "0000";
            string lastNo = string.Empty;
            string kodeSO;
            if (res.Length <= 4)
            {
                lastNo = formatSO;
                kodeSO = res;
            }
            else
            {
                lastNo = res.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries)[3];
                kodeSO = res.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries)[0];
            }
            string soNum = formatSO.Substring(0, formatSO.Length - (int.Parse(lastNo) + 1).ToString().Length) +
                           (int.Parse(lastNo) + 1);

            return kodeSO + "/" + costCenter.CostCenterId + "/" + DateTime.Now.Year + "/" + soNum;
        }

        public bool DeleteDetail(SalesReturnDetailDTO SalesReturnDetailDto)
        {
            bool res;
            try
            {
                var SRDetail =
                    SalesReturnRepository.Session.Get<SalesReturnDetail>(SalesReturnDetailDto.SalesReturnDetailId);
                SalesReturnRepository.ExecuteTransactionNative(
                    () =>
                        {
                            _mutasiStokRepository.DeleteMutasiStok(SalesReturnDetailDto.SRNo,
                                                                   SalesReturnDetailDto.Inventory.SerialNumber);
                            SalesReturnRepository.DeleteDetail(SRDetail, SalesReturnDetailDto.SalesReturnDetailId);
                        }
                    );
                res = true;
            }
            catch (Exception)
            {
                res = false;
            }

            return res;
        }


        public bool SendEmail(SalesReturnDTO obj, String DestinationFolder)
        {
            var mailServices = new MailServices();
            SalesReturn Obj = SalesReturnRepository.GetDataOne(x => x.SRNo == obj.SRNo);
            obj = Mapper.Map(Obj, obj);
            return mailServices.SendEmail(obj,DestinationFolder);
        }

        public void UpdateStatus(SalesReturnDTO salesReturnDto, Status status)
        {
            SalesReturnRepository.UpdateStatus(salesReturnDto.SRNo, (int)status);
        }


        public void CreateJurnal(SalesReturn salesReturn)
        {
            //Piutang
            Jurnal jurnal = new Jurnal();
            jurnal.NoVoucher = salesReturn.SRNo;
            jurnal.TglJurnal = salesReturn.SRDate;
            jurnal.Description = salesReturn.Description;
            jurnal.ReffNumber = salesReturn.SRNo;
            jurnal.TipeJurnal = typeof (SalesReturn).Name;


            decimal afterDisc = 0;
            if (salesReturn.SalesInvoice.DiscountType == "%")
                afterDisc = salesReturn.SalesReturnDetails.Sum(x => x.TotalDetail) -
                            (salesReturn.SalesReturnDetails.Sum(x => x.TotalDetail) * salesReturn.SalesInvoice.Discount / 100);
            else
                afterDisc = salesReturn.SalesReturnDetails.Sum(x => x.TotalDetail) - salesReturn.SalesInvoice.Discount;

                     
            foreach (var invoiceDetail in salesReturn.SalesReturnDetails)
            {
                //Penjualan
                if (string.IsNullOrEmpty(invoiceDetail.Inventory.Item.CoaSales)) throw new Exception(invoiceDetail.Inventory.SerialNumber + " belum memiliki chart of account SalesReturn!");
                jurnal.JurnalDetails.Add(new JurnalDetail() { AccountNumber = invoiceDetail.Inventory.Item.CoaSalesReturn, Departemen = salesReturn.CostCenter.Departemen, Debit = invoiceDetail.TotalDetail, Jurnal = jurnal, Description = invoiceDetail.Inventory.Item.CoaSalesReturn });
                //hpp
                var hpp = _hppRepository.FindLastHpp(invoiceDetail.Inventory.SerialNumber, salesReturn.SRDate);
                if (hpp.QtyAkhir == 0) throw new Exception(invoiceDetail.Inventory.SerialNumber + " belum ada hppnya!");
                if (string.IsNullOrEmpty(invoiceDetail.Inventory.Item.CoaHpp)) throw new Exception(invoiceDetail.Inventory.SerialNumber + " belum memiliki chart of account Hpp!");
                jurnal.JurnalDetails.Add(new JurnalDetail() { AccountNumber = invoiceDetail.Inventory.Item.CoaHpp, Departemen = salesReturn.CostCenter.Departemen, Kredit = hpp.HppAkhir * invoiceDetail.QtyUnit, Jurnal = jurnal, Description = invoiceDetail.Inventory.Item.CoaHpp });
                //inventory
                if (string.IsNullOrEmpty(invoiceDetail.Inventory.Item.CoaInventory)) throw new Exception(invoiceDetail.Inventory.SerialNumber + " belum memiliki chart of account inventory!");
                jurnal.JurnalDetails.Add(new JurnalDetail() { AccountNumber = invoiceDetail.Inventory.Item.CoaInventory, Departemen = salesReturn.CostCenter.Departemen, Debit = hpp.HppAkhir * invoiceDetail.QtyUnit, Jurnal = jurnal, Description = invoiceDetail.Inventory.Item.CoaInventory });
            }

        


            List<JurnalDetail> results = new List<JurnalDetail>();
            //Piutang 
            if (salesReturn.Customer.Coa == null) throw new Exception(salesReturn.Customer.CustomerName + " belum memiliki chart of account!");
            results.Add(new JurnalDetail() { AccountNumber = salesReturn.Customer.Coa.AccountNumber, Departemen = salesReturn.CostCenter.Departemen, Kredit = salesReturn.SalesInvoice.Ppn == 0 ? afterDisc : afterDisc + salesReturn.SalesInvoice.Ppn, Jurnal = jurnal });

            foreach (var gjd in jurnal.JurnalDetails.GroupBy(x => x.Description))
            {
                results.Add(new JurnalDetail()
                {
                    AccountNumber = jurnal.JurnalDetails.FirstOrDefault(x => x.Description == gjd.Key).AccountNumber,
                    Debit = jurnal.JurnalDetails.Where(x => x.Description == gjd.Key).Sum(x => x.Debit),
                    Kredit = jurnal.JurnalDetails.Where(x => x.Description == gjd.Key).Sum(x => x.Kredit),
                    Departemen = jurnal.JurnalDetails.FirstOrDefault(x => x.Description == gjd.Key).Departemen,
                    Description = jurnal.JurnalDetails.FirstOrDefault(x => x.Description == gjd.Key).Description,
                    Jurnal = jurnal
                });
            }
            if (salesReturn.SalesInvoice.Ppn > 0)
            {
                var coaPPn = _coaRepository.GetDataOne(x => x.AccountName.Contains("ppn"));
                if (coaPPn == null) throw new Exception("Chart of Account PPN belum ada!");
                results.Add(new JurnalDetail() { AccountNumber = coaPPn.AccountNumber, Departemen = salesReturn.CostCenter.Departemen, Debit = salesReturn.SalesInvoice.Ppn, Jurnal = jurnal });
            }
            jurnal.JurnalDetails = results;

            _jurnalRepository.SaveOrUpdate(jurnal);
            var hppServices = new HppServices();
            hppServices.CreateHpp(salesReturn);
        }


        public void Posting(IEnumerable<SalesReturnDTO> SalesReturnDtos)
        {
            SalesReturnRepository.ExecuteTransactionNative(() =>
            {
                foreach (var SalesReturnDto in SalesReturnDtos)
                {
                    var sr = SalesReturnRepository.GetDataOne(x => x.SRNo == SalesReturnDto.SRNo);
                    SalesReturnRepository.UpdateStatus(SalesReturnDto.SRNo, 3);
                    CreateJurnal(sr);
                }
            });

        }


        public void UnPosting(IEnumerable<SalesReturnDTO> SalesReturnDtos)
        {
            SalesReturnRepository.ExecuteTransactionNative(() =>
            {
                foreach (var SalesReturnDto in SalesReturnDtos)
                {
                    var SalesReturn = SalesReturnRepository.GetDataOne(x => x.SRNo == SalesReturnDto.SRNo);
                    SalesReturnRepository.UpdateStatus(SalesReturn.SRNo, 1);
                    var jurnal = _jurnalRepository.GetDataOne(x => x.NoVoucher == SalesReturnDto.SRNo);
                    _jurnalRepository.Delete(jurnal);
                    _hppRepository.DeleteHpp(SalesReturnDto.SRNo);
                }
            });

        }

        public IEnumerable<SalesReturnDTO> GetListByPeriode(DateTime tglstart, DateTime tglend)
        {
            List<SalesReturn> doElses = SalesReturnRepository.GetDataList(x => x.SRDate >= tglstart && x.SRDate <= tglend).ToList();
            var results = new List<SalesReturnDTO>();

            foreach (var doElse in doElses)
            {
                var doElseDTO = new SalesReturnDTO();
                doElseDTO.InjectFrom(doElse);
                results.Add(doElseDTO);
            }

            return results;
        }


        #endregion
    }
}